package elderberryni2.data;

import elderberryni2.abstracts.BaseTimestampable;
import java.util.ArrayList;

/**
 *
 * Extension of the normal Array List.
 * The List includes the BaseTimestampable behaviour and a maxTimeDifference that
 * allows to include only entries
 * 
 * @param <E> extends BaseTimestampable
 * @param <N> extends Number
 * @author warringer
 * @version 1.0
 */
public class TimeableArrayList<E extends BaseTimestampable<N> & Comparable<E>, N extends Number> 
    extends ArrayList<E> {
    
    private N maxTimeDifference;
    private N minTimeDifference;
    
    public TimeableArrayList() {
        super();
    }

    /**
     * Returns the maximum time difference between the first and the last List 
     * Entry
     * 
     * @return maxTimeDifference 
     */
    public N getMaxTimeDifference() {
        return maxTimeDifference;
    }

    /**
     * Sets the maximum time difference between the first and the last List
     * Entry
     * 
     * @param maxTimeDifference 
     */
    public void setMaxTimeDifference(N maxTimeDifference) {
        this.maxTimeDifference = maxTimeDifference;
    }
    
        /**
     * Returns the maximum time difference between the first and the last List 
     * Entry
     * 
     * @return maxTimeDifference 
     */
    public N getMinTimeDifference() {
        return minTimeDifference;
    }

    /**
     * Sets the maximum time difference between the first and the last List
     * Entry
     * 
     * @param maxTimeDifference 
     */
    public void setMinTimeDifference(N minTimeDifference) {
        this.minTimeDifference = minTimeDifference;
    }
    
    /**
     * Checks if the added Element is equal to the previously added Element
     * If yes, the timestamp of the previously added Element is set to the 
     * timestamp of the current Element.
     * If not, the Element is added and every element that is maxTimeDifference
     * older then the current Entry is deleted.
     * 
     * @param e (extends BaseTimestampable)
     * @return boolean
     */
    @Override
    public boolean add(E e) {
        if (size() == 0) {
            super.add(e);
            return true;
        } else {
            E latestEntry = super.get(super.size()-1);
            E eldestEntry = super.get(0);
            N timeDifferenceEldest = eldestEntry.getTimestampDifference(e.getTimestamp());
            N timeDifferenceLatest = latestEntry.getTimestampDifference(e.getTimestamp());
            if (latestEntry.compareTo(e) == 0) {
                latestEntry.setTimestamp(e.getTimestamp());
                return true;
            } else {
                if (super.size() > 2) {
                    while (compare(timeDifferenceEldest, maxTimeDifference) > 0) {
                        super.remove(eldestEntry);
                        eldestEntry = super.get(0);
                        timeDifferenceEldest = eldestEntry.getTimestampDifference(e.getTimestamp());
                    }
                }
                if (compare(timeDifferenceLatest, minTimeDifference) > 0 && super.size() > 0) {
                    super.add(e);
                } else {
                    if (super.isEmpty()) {
                        super.add(e);
                    }
                }
                return true;
            }
        }
    }
    
    /**
     * Internal Comparation between Numbers using compare syntax and values
     * 
     * @param Number n1
     * @param Number n2
     * @return compareTo values
     */
    private int compare(N n1, N n2) {
        if (n1 instanceof Float || n1 instanceof Double ) {
            double n3 = n1.doubleValue();
            double n4 = n2.doubleValue();
            if (n3 > n4) {
                return 1;
            } else if (n3 < n4) {
                return -1; 
            } else {
                return 0;
            }
        } else if (n1 instanceof Integer || n1 instanceof Byte || 
                n1 instanceof Short || n1 instanceof Long ) {
            long n3 = n1.longValue();
            long n4 = n2.longValue();
            if (n3 > n4) {
                return 1;
            } else if (n3 < n4) {
                return -1; 
            } else {
                return 0;
            }
        } else {
            return 0; 
        }
    }
}
